#include "screen.h"

void putch(uint8_t character, int row, int col, uint8_t attribute) {
	uint16_t* vidmemLoc = (uint16_t*)VIDEO_ADDRESS;
	int offset;	

	if(attribute == 0)
		attribute = WHITE_ON_BLACK;
	
	if(row >= 0 && col >= 0)
		offset = getOffset(row, col);
	else
		offset = get_cursor();

	vidmemLoc[offset] = character | (attribute<<8);
	
	offset++;
	offset = handle_scrolling(offset);
	set_cursor(offset);
}

int getOffset(int row, int col) {
	return (row * MAX_COLUMNS + col);
}

void cls() {
	int row;
	int col;
	for(row = 0; row < MAX_ROWS; row++) {
		for(col = 0; col < MAX_COLUMNS; col++) {
			putch(' ', row, col, 0);
		}
	}
	set_cursor(0);
}

void putstr_at(char* message, int row, int col, uint8_t attribute) {
	if(attribute == 0)
		attribute = WHITE_ON_BLACK;

	while(*message != 0){
		putch(*message, row, col, attribute);
		message++;
		col++;
	}
}

void putstr(char* message) {
	putstr_at(message, -1, -1, 0);
}

int get_cursor() {
	// read the cursor location from the VGA registers
	port_byte_out(REG_SCREEN_CTRL, 14);
	int offset = port_byte_in(REG_SCREEN_DATA) << 8;
	port_byte_out(REG_SCREEN_CTRL, 15);
	offset += port_byte_in(REG_SCREEN_DATA);

	return offset;
}

void set_cursor(int offset) {
	port_byte_out(REG_SCREEN_CTRL, 14);
	port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));
	port_byte_out(REG_SCREEN_CTRL, 15);
	port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xFF)); // the &0xff might not be needed
}

int handle_scrolling(int offset) {
	if(offset < MAX_ROWS*MAX_COLUMNS){
		return offset;
	}

	int i;
	for(i = 1; i < MAX_ROWS; i++) {
		memory_copy(VIDEO_ADDRESS + getOffset(i, 0), VIDEO_ADDRESS + getOffset(i+1, 0), MAX_COLUMNS*2);
	}
	
	uint16_t* last_line = (uint16_t*)(VIDEO_ADDRESS + getOffset(MAX_ROWS-1, 0));
	for(i = 0; i < MAX_COLUMNS; i++) {
		last_line[i] = ' ' | WHITE_ON_BLACK<<8;
	}

	return (offset-MAX_COLUMNS);
}
